package top.zhangjianyong.blog.aspect;

import com.alibaba.fastjson2.JSONObject;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import top.zhangjianyong.blog.cache.ApiCache;
import top.zhangjianyong.blog.entity.po.RequestLog;
import top.zhangjianyong.blog.response.ApiResponse;
import top.zhangjianyong.blog.response.PageDTO;
import top.zhangjianyong.blog.service.IpInfoService;
import top.zhangjianyong.blog.service.RequestLogService;
import top.zhangjianyong.blog.service.UserAgentInfoService;
import top.zhangjianyong.blog.utils.RequestUtil;
import java.util.*;

/**
 * 客户端信息
 *
 * @author zhangjianyong
 * @since 2024/8/27 上午9:40
 */
@Slf4j
@Aspect
@Component
@Order(1)
public class ClientAspect {

    private final RequestLogService requestLogService;

    // 响应内容最大长度限制（字符数）
    private static final int MAX_RESPONSE_LENGTH = 2000;

    // 需要简化日志的方法模式
    private static final Set<String> SIMPLIFIED_LOG_PATTERNS =
        Set.of("page", "list", "tree", "topList", "get");

    // 敏感字段，不记录到日志中
    private static final Set<String> SENSITIVE_FIELDS =
        Set.of("password", "token", "secret", "key", "credential");
    private final IpInfoService ipInfoService;
    private final UserAgentInfoService userAgentInfoService;

    public ClientAspect(RequestLogService requestLogService, IpInfoService ipInfoService, UserAgentInfoService userAgentInfoService) {
        this.requestLogService = requestLogService;
        this.ipInfoService = ipInfoService;
        this.userAgentInfoService = userAgentInfoService;
    }

    @Pointcut(
        "execution(public * top.zhangjianyong.blog.portal.controller.*.*(..))"
            + "|| execution(public * top.zhangjianyong.blog.manage.controller.*.*(..))")
    public void pointCut() {}

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        try {
            log.info("clientAspect ...");
            String ip = RequestUtil.getClientIP();
            log.info("ip: {}", ip);
            String userAgent = RequestUtil.getClientUserAgent();
            log.info("userAgent: {}", userAgent);
            String visitorUuid = RequestUtil.getVisitUUID();
            log.info("visitorUuid: {}", visitorUuid);
            String name = pjp.getSignature().getDeclaringType().getName();
            boolean isPortal = !name.startsWith("top.zhangjianyong.blog.manage.controller");
            ApiCache.set(ip, isPortal, userAgent, visitorUuid);
            return pjp.proceed();
        } finally {
            ApiCache.remove();
            // 对象池模式下，DatabaseReader实例会自动归还到池中，无需手动清理
        }
    }

    @AfterReturning(pointcut = "pointCut()", returning = "result")
    public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
        printJoinPoint(joinPoint);

        // 智能日志记录
        String resultSummary = createResultSummary(joinPoint, result);
        log.info("Method result summary: {}", resultSummary);

        RequestLog requestLog = buildRequestLog(joinPoint, result);
        requestLogService.save(requestLog);

        // 保存IP信息
        ipInfoService.saveOrUpdate(requestLog.getIp());
        // 保存userAgent信息
        userAgentInfoService.saveOrUpdate(requestLog.getUserAgent());
    }

    @AfterThrowing(pointcut = "pointCut()", throwing = "ex")
    public void afterThrowingAdvice(JoinPoint joinPoint, Exception ex) {
        printJoinPoint(joinPoint);
        log.info("Exception: {}", ex.getMessage());
        RequestLog requestLog = buildRequestLog(joinPoint, ex);
        requestLogService.save(requestLog);

        // 保存IP信息
        ipInfoService.saveOrUpdate(requestLog.getIp());
        // 保存userAgent信息
        userAgentInfoService.saveOrUpdate(requestLog.getUserAgent());
    }

    /** 创建响应结果摘要 */
    private String createResultSummary(JoinPoint joinPoint, Object result) {
        if (result == null) {
            return "null";
        }

        String methodName = joinPoint.getSignature().getName().toLowerCase(Locale.ROOT);

        // 检查是否需要简化日志
        boolean needSimplify = SIMPLIFIED_LOG_PATTERNS.stream().anyMatch(methodName::contains);

        if (!needSimplify) {
            // 不需要简化的方法，直接返回完整结果（但仍有长度限制）
            String fullResult = JSONObject.toJSONString(result);
            return truncateString(fullResult);
        }

        // 需要简化的方法，创建摘要
        return createDetailedSummary(result);
    }

    /** 创建详细摘要 */
    private String createDetailedSummary(Object result) {
        if (result instanceof ApiResponse apiResponse) {
            Map<String, Object> summary = new LinkedHashMap<>();

            summary.put("code", apiResponse.getCode());
            summary.put("message", apiResponse.getMessage());

            Object data = apiResponse.getData();
            if (data != null) {
                summary.put("dataType", data.getClass().getSimpleName());
                summary.put("dataSummary", createDataSummary(data));
            }

            return JSONObject.toJSONString(summary);
        }

        // 非ApiResponse类型的响应
        return createDataSummary(result);
    }

    /** 创建数据摘要 */
    private String createDataSummary(Object data) {
        if (data == null) {
            return "null";
        }

        if (data instanceof Collection<?> collection) {
            Map<String, Object> summary = new LinkedHashMap<>();
            summary.put("type", "Collection");
            summary.put("size", collection.size());

            if (!collection.isEmpty()) {
                Object firstItem = collection.iterator().next();
                summary.put("itemType", firstItem.getClass().getSimpleName());
                summary.put("firstItemSample", createObjectSummary(firstItem));
            }

            return JSONObject.toJSONString(summary);
        }

        if (data instanceof PageDTO<?> pageData) {
            Map<String, Object> summary = new LinkedHashMap<>();
            summary.put("type", "PageData");
            summary.put("current", pageData.getCurrent());
            summary.put("size", pageData.getSize());
            summary.put("total", pageData.getTotal());
            summary.put("pages", pageData.getPages());

            List<?> records = pageData.getRecords();
            if (records != null && !records.isEmpty()) {
                summary.put("recordCount", records.size());
                summary.put("recordType", records.get(0).getClass().getSimpleName());
                summary.put("firstRecordSample", createObjectSummary(records.get(0)));
            }

            return JSONObject.toJSONString(summary);
        }

        if (data instanceof Map<?, ?> map) {
            Map<String, Object> summary = new LinkedHashMap<>();
            summary.put("type", "Map");
            summary.put("size", map.size());
            summary.put("keys", new ArrayList<>(map.keySet()));

            return JSONObject.toJSONString(summary);
        }

        // 对于复杂对象，创建字段摘要
        return createObjectSummary(data);
    }

    /** 创建对象摘要 */
    private String createObjectSummary(Object obj) {
        if (obj == null) {
            return "null";
        }

        // 基本类型直接返回
        if (isPrimitiveOrWrapper(obj.getClass()) || obj instanceof String) {
            return String.valueOf(obj);
        }

        try {
            String jsonStr = JSONObject.toJSONString(obj);

            // 如果JSON字符串较短，直接返回
            if (jsonStr.length() <= 200) {
                return jsonStr;
            }

            // 对于较长的JSON，提取关键字段
            JSONObject jsonObj = JSONObject.parseObject(jsonStr);
            Map<String, Object> summary = new LinkedHashMap<>();

            // 提取常见的关键字段
            String[] keyFields = {"id", "title", "name", "type", "status", "createTime", "updateTime"};
            for (String field : keyFields) {
                if (jsonObj.containsKey(field)) {
                    summary.put(field, jsonObj.get(field));
                }
            }

            // 添加字段统计
            summary.put("_totalFields", jsonObj.size());
            summary.put("_objectType", obj.getClass().getSimpleName());

            return JSONObject.toJSONString(summary);

        } catch (RuntimeException e) {
            // JSON序列化失败，返回类型信息
            return String.format(
                "{\"_objectType\":\"%s\",\"_error\":\"Failed to serialize\"}",
                obj.getClass().getSimpleName());
        }
    }

    /** 检查是否为基本类型或包装类型 */
    private boolean isPrimitiveOrWrapper(Class<?> clazz) {
        return clazz.isPrimitive()
            || clazz == Boolean.class
            || clazz == Character.class
            || clazz == Byte.class
            || clazz == Short.class
            || clazz == Integer.class
            || clazz == Long.class
            || clazz == Float.class
            || clazz == Double.class;
    }

    /** 截断字符串到指定长度 */
    private String truncateString(String str) {
        if (str == null || str.length() <= ClientAspect.MAX_RESPONSE_LENGTH) {
            return str;
        }
        return str.substring(0, ClientAspect.MAX_RESPONSE_LENGTH) + "...[truncated]";
    }

    /** 过滤敏感参数 */
    private List<Object> getArgs(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        return Arrays.stream(args)
            .filter(arg -> !(arg instanceof MultipartFile))
            .filter(arg -> !(arg instanceof HttpServletResponse))
            .map(this::filterSensitiveData)
            .toList();
    }

    /** 过滤敏感数据 */
    private Object filterSensitiveData(Object obj) {
        if (obj == null) {
            return null;
        }

        try {
            String jsonStr = JSONObject.toJSONString(obj);
            JSONObject jsonObj = JSONObject.parseObject(jsonStr);

            // 移除敏感字段
            for (String sensitiveField : SENSITIVE_FIELDS) {
                if (jsonObj.containsKey(sensitiveField)) {
                    jsonObj.put(sensitiveField, "***");
                }
            }

            return jsonObj;
        } catch (RuntimeException e) {
            // 如果不能序列化为JSON，直接返回原对象
            return obj;
        }
    }

    private void printJoinPoint(JoinPoint joinPoint) {
        String fullSignature = joinPoint.getSignature().toLongString();
        log.info("Full method signature: {}", fullSignature);

        List<Object> filteredArgs = getArgs(joinPoint);
        String argsStr = JSONObject.toJSONString(filteredArgs);
        log.info("Method arguments: {}", truncateString(argsStr));
    }

    private RequestLog buildRequestLog(JoinPoint joinPoint, Object result) {
        RequestLog requestLog = buildRequestLog(joinPoint, 0);

        // 对结果进行智能处理
        String resultStr = createResultSummary(joinPoint, result);
        requestLog.setResult(truncateString(resultStr));

        if (result instanceof ApiResponse) {
            requestLog.setCode(((ApiResponse) result).getCode());
        }
        return requestLog;
    }

    private RequestLog buildRequestLog(JoinPoint joinPoint, Exception ex) {
        RequestLog requestLog = buildRequestLog(joinPoint, 1);
        // 截取异常的长度为500
        String exceptionMsg = ex.getMessage();
        if (exceptionMsg != null && exceptionMsg.length() > 500) {
            exceptionMsg = exceptionMsg.substring(0, 500) + "...[truncated]";
        }
        requestLog.setException(exceptionMsg);
        return requestLog;
    }

    private RequestLog buildRequestLog(JoinPoint joinPoint, int status) {
        RequestLog requestLog = new RequestLog();
        String ip = RequestUtil.getClientIP();
        String userAgent = RequestUtil.getClientUserAgent();
        requestLog.setIp(ip);
        requestLog.setUserAgent(userAgent);
        requestLog.setUri(RequestUtil.getUri());
        requestLog.setMethod(RequestUtil.getMethod());
        requestLog.setSignature(joinPoint.getSignature().toLongString());

        // 对参数进行智能处理
        List<Object> filteredArgs = getArgs(joinPoint);
        String paramsStr = JSONObject.toJSONString(filteredArgs);
        requestLog.setParams(truncateString(paramsStr));

        requestLog.setStatus(status);
        requestLog.setOrigin(ApiCache.get().isPortal() ? 1 : 0);

        return requestLog;
    }
}
